package android.rockchip.update.service;

import android.app.AlertDialog;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.StatFs;
import android.rockchip.update.RomUpdate;
import android.rockchip.update.download.DownloadRom;
import android.rockchip.update.tasks.GenerateRecoveryScript;
import android.rockchip.update.utils.CommonDialog;
import android.rockchip.update.utils.CustomerHttpClient;
import android.rockchip.update.utils.Preferences;
import android.text.TextUtils;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Button;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpHead;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Class  Name: RKUpdateService
 * Description:
 * Created by bruce on 17/10/9
 */
public class RKUpdateService extends Service {

    private static final String TAG = "RKUpdateService";
    private static final boolean mIsSupportUsbUpdate = true;

    public static String OTA_PACKAGE_FILE = "update.zip";
    private static volatile boolean mWorkHandleLocked = false;
    private static volatile boolean mIsNeedDeletePackage = false;

    public static final String EXTRA_ERR_MESSAGE = "android.rockchip.update.extra.ERR_MESSAGE";
    public static final String EXTRA_IMAGE_PATH = "android.rockchip.update.extra.IMAGE_PATH";
    public static final String DATA_ROOT = "/data/media/0/";
    public static final String FLASH_ROOT = "/sdcard/";
    public static final String SDCARD_ROOT = "/mnt/external_sd/";
    public static final String USB_ROOT = "/mnt/usb_storage/";

    public static final int COMMAND_NULL = 0;
    public static final int COMMAND_CHECK_LOCAL_UPDATING = 1;
    public static final int COMMAND_CHECK_REMOTE_UPDATING = 2;
    public static final int COMMAND_DELETE_UPDATEPACKAGE = 3;

    private static final String COMMAND_FLAG_SUCCESS = "success";
    private static final String COMMAND_FLAG_UPDATING = "updating";

    private static final String[] IMAGE_FILE_DIRS = {
            DATA_ROOT,
            FLASH_ROOT,
            SDCARD_ROOT,
            USB_ROOT,
    };

    private Context mContext;
    private volatile boolean mIsFirstStartUp = true;

    private String mLastUpdatePath;
    private WorkHandler mWorkHandler;
    private Handler mMainHandler;
    public static URI mRemoteURI = null;
    private String mTargetURI = null;
    private String mOtaPackageVersion = null;
    private String mOtaPackageName = null;
    private String mOtaPackageLength = null;
    private String mOtaDescription = "";
    private CommonDialog dialog;

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if ("android.rockchip.update.service.DOWNLOADFINISH".equals(intent.getAction()))
                showInstallDialog();
            else if ("android.rockchip.update.service.INSTALLNOW".equals(intent.getAction())) {
                Log.d(TAG, "broadcastReceiver android.rockchip.update.service.INSTALLNOW");
                new GenerateRecoveryScript(mContext).execute();
                if (timer != null) timer.cancel();
            }
        }
    };

    private class WorkHandler extends Handler {
        private static final int NOT_NOTIFY_IF_NO_IMG = 0;

        public WorkHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message msg) {
            String[] searchResult;

            switch (msg.what) {
                case COMMAND_CHECK_LOCAL_UPDATING:
                    LOG("WorkHandler::handleMessage() : To perform 'COMMAND_CHECK_LOCAL_UPDATING'.");
                    if (mWorkHandleLocked) {
                        LOG("WorkHandler::handleMessage() : locked !!!");
                        return;
                    }

                    if (null != (searchResult = getValidFirmwareImageFile(IMAGE_FILE_DIRS))) {
                        if (1 == searchResult.length) {
                            final String path = searchResult[0];
                            LOG("WorkHandler::handleMessage() : find package,path = " + path);
                            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
                            builder.setMessage(R.string.available_reboot_confirm)
                                    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            Log.d(TAG, "发现本地升级包，确认升级");
                                            if (!TextUtils.isEmpty(path)) {
                                                if (path.contains("sdcard")) {
                                                    String newPath = path.replace("sdcard", "mnt/internal_sd");
                                                    new GenerateRecoveryScript(mContext, newPath).execute();
                                                } else
                                                    new GenerateRecoveryScript(mContext, path).execute();
                                            }
                                        }
                                    }).setNegativeButton(R.string.cancel, null);
                            AlertDialog updateDialog = builder.create();
                            updateDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                            updateDialog.setCanceledOnTouchOutside(false); //点击外面区域不会让dialog消失
                            updateDialog.show();
                            return;
                        } else {
                            LOG("find more than two package files, so it is invalid!");
                            return;
                        }
                    } else {
                        LOG("Not find image file!");
                    }
                    break;
                case COMMAND_CHECK_REMOTE_UPDATING:
                    if (mWorkHandleLocked) {
                        LOG("WorkHandler::handleMessage() : locked !!!");
                        return;
                    }

                    try {
                        boolean result = requestRemoteServerForUpdate(mRemoteURI);
                        LOG("requestRemoteServerForUpdate = " + result);
                        if (result) {
                            RomUpdate.setRomUrl(mContext, mRemoteURI.toString());
                            downloadRom();
                            mWorkHandleLocked = true;
                        }
                        break;
                    } catch (Exception e) {
                        LOG("COMMAND_CHECK_REMOTE_UPDATING error," + Arrays.toString(e.getStackTrace()));
                    }

                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        LOG("sleep fail.");
                    }
                    break;
                case COMMAND_DELETE_UPDATEPACKAGE:
                    if (mIsNeedDeletePackage) {
                        LOG("execute COMMAND_DELETE_UPDATEPACKAGE...");
                        File f = new File(mLastUpdatePath);
                        if (f.exists()) {
                            f.delete();
                            LOG("delete complete! path=" + mLastUpdatePath);
                        }
                        mIsNeedDeletePackage = false;
                        mWorkHandleLocked = false;
                    }
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.rockchip.update.service.DOWNLOADFINISH");
        filter.addAction("android.rockchip.update.service.INSTALLNOW");
        registerReceiver(broadcastReceiver, filter);

        try {
            mRemoteURI = new URI(getRemoteUri());
            LOG("remote uri is " + mRemoteURI.toString());
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        mMainHandler = new Handler(Looper.getMainLooper());
        HandlerThread workThread = new HandlerThread("UpdateService : work thread");
        workThread.start();
        mWorkHandler = new WorkHandler(workThread.getLooper());

        if (mIsFirstStartUp) {
            LOG("first startup!!!");
            mIsFirstStartUp = false;
            String command = RecoverySystem.readFlagCommand();
            String path;
            if (command != null) {
                LOG("command = " + command);
                if (command.contains("$path")) {
                    path = command.substring(command.indexOf('=') + 1);
                    LOG("last_flag: path = " + path);

                    if (command.startsWith(COMMAND_FLAG_SUCCESS)) {//更新成功
                        mIsNeedDeletePackage = true;
                        mLastUpdatePath = path;

                        LOG("now try to start notifydialog activity!");
                        mWorkHandleLocked = true;
                        return;
                    }
                    if (command.startsWith(COMMAND_FLAG_UPDATING)) {//更新失败
                        mWorkHandleLocked = true;
                    }
                }
            }
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LOG("onStartCommand……");
        if (intent == null) return Service.START_NOT_STICKY;

        int command = intent.getIntExtra("command", COMMAND_NULL);
        int delayTime = intent.getIntExtra("delay", 1000);
        LOG("onStartCommand:command = " + command + " delayTime = " + delayTime);

        if (command == COMMAND_NULL) {
            return Service.START_NOT_STICKY;
        }

        if (mIsNeedDeletePackage) {
            command = COMMAND_DELETE_UPDATEPACKAGE;
            delayTime = 20000;
            mWorkHandleLocked = true;
        }

        Message msg = new Message();
        msg.what = command;
        msg.arg1 = WorkHandler.NOT_NOTIFY_IF_NO_IMG;
        mWorkHandler.sendMessageDelayed(msg, delayTime);
        return Service.START_REDELIVER_INTENT;
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(broadcastReceiver);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        LOG("onStart.......");
        super.onStart(intent, startId);
    }

    private boolean requestRemoteServerForUpdate(URI remote) throws IOException {
        if (remote == null) return false;

        HttpClient httpClient = CustomerHttpClient.getHttpClient();
        HttpHead httpHead = new HttpHead(remote);

        HttpResponse response = httpClient.execute(httpHead);
        int statusCode = response.getStatusLine().getStatusCode();

        if (statusCode != 200) return false;

        StringBuilder headStr = new StringBuilder();
        for (Header header : response.getAllHeaders()) {
            headStr.append(header.getName()).append(":").append(header.getValue()).append("\n");
        }
        LOG(headStr.toString());

        Header[] headLength = response.getHeaders("OtaPackageLength");
        if (headLength != null && headLength.length > 0) {
            mOtaPackageLength = headLength[0].getValue();
        }

        if (mOtaPackageLength == null || TextUtils.equals(mOtaPackageLength, "null")
                || TextUtils.equals(mOtaPackageLength, "0")) return false;
        if (getAvailableBytes() <= Long.valueOf(mOtaPackageLength)) {
            Log.e(TAG, " Download update package fail,No enough space!");
            return false;
        }

        Header[] headName = response.getHeaders("OtaPackageName");
        if (headName == null) return false;

        if (headName.length > 0) {
            mOtaPackageName = headName[0].getValue();
        }

        Header[] headVersion = response.getHeaders("OtaPackageVersion");
        if (headVersion != null && headVersion.length > 0) {
            mOtaPackageVersion = headVersion[0].getValue();
        }

        Header[] headTargetURI = response.getHeaders("OtaPackageUri");
        if (headTargetURI == null) return false;

        Header[] otaDescription = response.getHeaders("OtaDescription");
        if (otaDescription != null && otaDescription.length > 0) {
            mOtaDescription = URLDecoder.decode(otaDescription[0].getValue(), "UTF-8");
            Preferences.setUpdateDescription(mContext, mOtaDescription);
        }

        if (headTargetURI.length > 0) {
            mTargetURI = headTargetURI[0].getValue();
        }

        if (mOtaPackageName == null || mTargetURI == null) {
            LOG("server response format error!");
            return false;
        }

        if (!mTargetURI.startsWith("http://") && !mTargetURI.startsWith("https://")
                && !mTargetURI.startsWith("ftp://")) {
            mTargetURI = "http://" + getRemoteHost() + (mTargetURI.startsWith("/")
                    ? mTargetURI : ("/" + mTargetURI));
        }
        RomUpdate.setVersionName(mContext, mOtaPackageName);
        RomUpdate.setFileSize(mContext, Integer.parseInt(mOtaPackageLength));

        LOG("OtaPackageName = " + mOtaPackageName + ",OtaPackageVersion = " + mOtaPackageVersion
                + ",OtaPackageLength = " + mOtaPackageLength + ",SystemVersion = " + getSystemVersion()
                + ",OtaPackageUri = " + mTargetURI + ",mOtaDescription" + mOtaDescription);
        return true;
    }

    private long getAvailableBytes() {
        //calculate flash available bytes
        StatFs stat = new StatFs(FLASH_ROOT);
        long blockSize = stat.getBlockSize();
        long availableBlocks = stat.getAvailableBlocks();
        long availableBytes = blockSize * availableBlocks;
        LOG("downloadImg() : 'blockSize' = " + blockSize + ", 'availableBlocks' = "
                + availableBlocks + "; Available FS space flash : " + availableBytes + " bytes");
        return availableBytes;
    }

    private String[] getValidFirmwareImageFile(String searchPaths[]) {
        for (String dirPath : searchPaths) {
            String filePath = dirPath + OTA_PACKAGE_FILE;
            LOG("getValidFirmwareImageFile() : Target image file path : " + filePath);
            if ((new File(filePath)).exists()) return (new String[]{filePath});
        }

        if (mIsSupportUsbUpdate) {
            //find usb device update package
            File usbRoot = new File(USB_ROOT);
            File[] pFiles = usbRoot.listFiles();
            if (pFiles == null) return null;

            for (File tmp : pFiles) {
                LOG("tmp : " + tmp);
                if (tmp.isDirectory()) {
                    File[] subFiles = tmp.listFiles();
                    for (File subTmp : subFiles) {
                        LOG("subtmp : " + subTmp);
                        if (subTmp.isDirectory()) {
                            File[] files = subTmp.listFiles(new FileFilter() {
                                @Override
                                public boolean accept(File arg0) {
                                    LOG("scan usb files: " + arg0.getAbsolutePath());
                                    if (arg0.isDirectory()) {
                                        LOG("arg0 is directory");
                                        return false;
                                    }

                                    if (arg0.getName().equals(OTA_PACKAGE_FILE)) {
                                        LOG("find update package");
                                        return true;
                                    } else {
                                        LOG("Not find update package");
                                    }
                                    return false;
                                }
                            });

                            if (files != null && files.length > 0) {
                                LOG("find update.zip in usb device, path = " + files[0].getAbsolutePath());
                                return new String[]{files[0].getAbsolutePath()};
                            } else {
                                LOG("Not find update.zip in usb device");
                            }
                        } else {
                            if (subTmp.getName().equals(OTA_PACKAGE_FILE)) {
                                LOG("find a update package, absolute path : " + subTmp.getAbsolutePath());
                                return new String[]{subTmp.getAbsolutePath()};
                            } else {
                                continue;
                            }
                        }
                    }
                } else if (tmp.isFile()) {
                    if (tmp.getName().equals(OTA_PACKAGE_FILE)) {
                        LOG("find a update package, absolute path : " + tmp.getAbsolutePath());
                        return new String[]{tmp.getAbsolutePath()};
                    } else {
                        continue;
                    }
                }
            }
        }
        return null;
    }

    /*********************************** ota update ****************************************/
    public String getRemoteUri() {
        return "http://" + getRemoteHost() + "/OtaUpdater/android?product=" + getOtaProductName()
                + "&version=" + getSystemVersion() + "&deviceId=" + getDid();
    }

    public String getRemoteHost() {
        String remoteHost = getSystemProperties("ro.product.ota.host");
        if (remoteHost == null || remoteHost.length() == 0) {
            remoteHost = "s.hohoapp.cn/j";
            LOG("getRemoteHost = " + remoteHost);
        }
//        remoteHost = "s.dev.hohoapp.cn/j";//dev地址，测试用
        return remoteHost;
    }

    public static String getOtaProductName() {
        String productName = getSystemProperties("ro.product.model");
        if (productName.contains(" ")) {
            productName = productName.replaceAll(" ", "");
        }
        return productName;
    }

    public static String getSystemVersion() {
        String version = getSystemProperties("ro.product.version");
        if (version == null || version.length() == 0) {
            version = "1.0.3";
        }

        return version;
    }

    public static String getCountry() {
        return Locale.getDefault().getCountry();
    }

    public static String getLanguage() {
        return Locale.getDefault().getLanguage();
    }

    public static String getSystemProperties(String key) {
        String result = "";
        try {
            Class<?> c = Class.forName("android.os.SystemProperties");

            Method get = c.getMethod("get", String.class);
            result = (String) get.invoke(c, key);

        } catch (ClassNotFoundException | NoSuchMethodException | IllegalArgumentException
                | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;
    }

    private void downloadRom() {
        DownloadRom mDownloadRom = new DownloadRom();
        String romUrl = RomUpdate.getRomUrl(mContext);
        LOG("romUrl = " + romUrl);

        if (!romUrl.equals("null") && !romUrl.isEmpty()) {
            LOG("Downloading via DownloadManager");
            mDownloadRom.startDownload(mContext);
        } else {
            LOG("No links found");
        }
    }

    private static void LOG(String msg) {
        Log.e(TAG, msg);
    }


    private String getDid() {
        String did = "1111111";
        try {
            Context boardContext = mContext.createPackageContext(
                    "com.w2here.board", Context.CONTEXT_IGNORE_SECURITY);
            SharedPreferences boardSp = boardContext.getSharedPreferences("hhapp_base_cache",
                    Context.MODE_MULTI_PROCESS);
            did = boardSp.getString("device_id", "222222");
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        LOG("return did = " + did);
        return did;
    }

    private void showInstallDialog() {
        setTimer();
        if (dialog == null)
            dialog = new CommonDialog.Builder(this)
                    .setTitle(getString(R.string.str_update_title))
                    .setMessage("hbo v" + mOtaPackageVersion + getString(R.string.str_update_message))
                    .setSubMessage(mOtaDescription)
                    .setCancelButton(getString(R.string.str_install_later),
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.cancel();
                                    if (timer != null) timer.cancel();
                                }
                            })
                    .setConfirmButton(getString(R.string.str_install_now),
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.cancel();
                                    if (timer != null) timer.cancel();
                                    new GenerateRecoveryScript(mContext).execute();
                                }
                            })
                    .create();
        mMainHandler.post(new Runnable() {
            public void run() {
                dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                if (!dialog.isShowing()) dialog.show();
            }
        });
    }

    Timer timer, countDown;

    private void setTimer() {
        if (timer == null) {
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    mMainHandler.post(new Runnable() {
                        public void run() {
                            new GenerateRecoveryScript(mContext).execute();
                        }
                    });
                    timer.cancel();
                }
            }, 180000);
        }

        if (countDown == null) {
            countDown = new Timer();
            countDown.schedule(new TimerTask() {
                int countTime = 180;
                Button button;

                @Override
                public void run() {
                    if (countTime > 0) {
                        mMainHandler.post(new Runnable() {
                            public void run() {
                                if (dialog != null) {
                                    button = (Button) dialog.findViewById(R.id.dialog_confirm);
                                    button.setText(getString(R.string.str_install_now) + "(" + countTime + ")");
                                }
                            }
                        });
                        countTime--;
                    } else
                        countDown.cancel();
                }
            }, 0, 1000);
        }
    }

}

